﻿using System;
using System.Collections.Generic;
using System.Reflection;
using CQRS.General;
using CQRS.Infrastructure.Hermes.Infrastructure.ServiceLocator;

namespace CQRS.Infrastructure
{
    public class MessageHandlersInMessageRouterRegistrator
    {
        private static MethodInfo _createPublishActionMethod;
        private static MethodInfo _registerMessageMethod;

        public static void BootStrap(Assembly assembly,
            Type messageType, Type messageHandlerType)
        {
            var messageRouter = IoC.Resolve<IMessageRouter>();
            new MessageHandlersInMessageRouterRegistrator().RegisterRoutes(assembly, messageRouter, messageType, messageHandlerType);
        }

        public void RegisterRoutes(Assembly assembly, IMessageRouter messageRouter,
            Type messageType, Type messageHandlerType)
        {
            _createPublishActionMethod = GetType().GetMethod("CreatePublishAction");
            _registerMessageMethod = messageRouter.GetType().GetMethod("Register");

            IEnumerable<Type> entities = HandlerHelper.GetEntities(assembly, messageType);
            IDictionary<Type, IList<Type>> entityHandlers =
                HandlerHelper.GetEntityHandlers(assembly, messageHandlerType);

            foreach (Type entity in entities)
            {
                IList<Type> handlerTypes;
                if (!entityHandlers.TryGetValue(entity, out handlerTypes))
                    throw new Exception(string.Format("No handlers found for message '{0}'", entity.FullName));

                foreach (Type handler in handlerTypes)
                {
                    object injectedHandler = GetCorrectlyInjectedHandler(handler);
                    object action = CreateTheProperAction(entity, injectedHandler);
                    RegisterTheCreatedActionWithTheMessageRouter(messageRouter, entity, action);
                }
            }
        }

        public Action<TMessage> CreatePublishAction<TMessage, TMessageHandler>(TMessageHandler messageHandler)
            where TMessage : class
            where TMessageHandler : class, IHandler<TMessage>
        {
            return messageHandler.Execute;
        }

        private static void RegisterTheCreatedActionWithTheMessageRouter(IMessageRouter messageRouter,
                                                                         Type messageType, object action)
        {
            _registerMessageMethod.MakeGenericMethod(messageType).Invoke(messageRouter, new[] { action });
        }
        private object CreateTheProperAction(Type messageType, object messageHandler)
        {
            var action = _createPublishActionMethod.MakeGenericMethod(messageType, messageHandler.GetType())
                .Invoke(this, new[] {messageHandler}) ;
            return action;
        }

        private static object GetCorrectlyInjectedHandler(Type messageHandler)
        {
            return IoC.Resolve(messageHandler);
        }
    }
}